/**
 * @desc rollup 默认配置文件
 * [rollup -c 打包指令](https://www.rollupjs.com/guide/big-list-of-options#%E6%A0%B8%E5%BF%83%E5%8A%9F%E8%83%BDcore-functionality)
 */
import pkg from './package.json';
const frameConfig = require('./frame.config.js');
import vuePlugin from 'rollup-plugin-vue'; // 打包构建 vue 文件
import css from 'rollup-plugin-css-only'; // 将css从组件中提取单独打包为一个.css后缀的文件
// import scss from 'rollup-plugin-scss';
// import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import resolve from '@rollup/plugin-node-resolve'; // 帮助 Rollup 查找外部模块，然后导入（rollup无法识别 node_modules 里面的包）
import commonjs from '@rollup/plugin-commonjs'; // 将CommonJS模块转换为 ES2015 供 Rollup 处理
import json from '@rollup/plugin-json'; // Rollup 从 JSON 文件中读取数据
// 类比 Webpack 的 DefinePlugin , 可在源码中通过 process.env.NODE_ENV 用于构建区分 Development 与 Production 环境.
import replace from '@rollup/plugin-replace'; // 替换待打包文件里的一些变量，如 process在浏览器端是不存在的，需要被替换
import babel from '@rollup/plugin-babel'; // rollup babel插件（使用es6新特性来编写代码）
import { terser } from 'rollup-plugin-terser'; // [代码压缩](https://github.com/trysound/rollup-plugin-terser#using-as-output-plugin)
import alias from '@rollup/plugin-alias'; // 将模块中’@'别名替换为’src’目录；
import eslint from '@rollup/plugin-eslint'; //  [eslint js代码检测](https://github.com/rollup/plugins/tree/master/packages/eslint)
import clear from 'rollup-plugin-clear'; // 清除目录
import fileSize from 'rollup-plugin-filesize'; // 显示 bundle 文件大小
import { visualizer } from 'rollup-plugin-visualizer'; // 打包分析插件 类似webpack-bundle-analyzer
// import postcss from 'rollup-plugin-postcss'; // 使用 rollup-plugin-vue 中的 postcss 配置，但是 rollup-plugin-postcss 和 postcss 插件还需要安装
import autoprefixer from 'autoprefixer'; // css 浏览器厂商前缀自动添加
import postcssImport from 'postcss-import'; // 支持 @import 的导入
import less from 'rollup-plugin-less'; // css 压缩和去除注释
import cssnano from 'cssnano';
const sizes = require('rollup-plugin-sizes');
const _concat = require('lodash/concat');
const _has = require('lodash/has');
import path from 'path';

var dayjs = require('dayjs');
var isDev = process.env.NODE_ENV === 'dev'; // 开发环境
const pathResolve = p => path.resolve(__dirname, p);

function createFileName(formatName) {
  return `dist/${frameConfig.outputFileNamePre}.${formatName}.js`;
}

const createBanner = () => {
  return `/*!
   * ${pkg.name} v${pkg.version}
   * (c) ${dayjs().format('YYYY')}
   * @license MIT
   */`;
};

// 基础配置
const createBaseConfig = () => {
  const plugins = [
    clear({ targets: ['dist'], watch: false }), // 清除上一次的构建dist目录
    // css({ output: `dist/${frameConfig.outputFileNamePre}.css` }), // 如果不单独生成css文件那么请把vuePlugin.css设置为true，这行配置应该放到eslint配置上面不然会监测生成的css会提示eslint错误
    eslint({
      fix: false,
      exclude: ['node_modules/**'],
      include: 'src/**'
    }),
    // peerDepsExternal(),
    babel({
      babelHelpers: 'runtime', // [使plugin-transform-runtime生效](https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers)
      exclude: 'node_modules/**' // 防止打包node_modules下的文件
    }),
    vuePlugin({
      css: !frameConfig.extractCss, // 使用配置css()插件则将css设置为false
      style: {
        postcssModulesOptions: {
          generateScopedName: isDev
            ? '[path]-[name]_[local]_[hash:base64:5]'
            : '[name]_[local]_[hash:base64:5]'
        },
        postcssOptions: {},
        postcssPlugins: [
          autoprefixer(),
          postcssImport(),
          cssnano({ preset: 'default' })
        ]
      },
      exclude: ['node_modules/**']
    }), // 构建 .vue 文件，不能放到resolve配置的下面不然 vue 的模板文件无法解析
    /* postcss({
      plugins: [autoprefixer(), postcssImport()],
      extensions: ['.css', '.sss', '.pcss', '.less']
     }), // 使用单独的 postcss 配置插件 autoprefixer 这样无法在css中添加厂商前缀，所以改用 vuePlugin 中的配置，postcssImport 插件时可以启用的 */
    less(),
    resolve({
      // extensions: ['.vue', '.jsx', '.js', '.json'] // 指定插件将对其进行操作的文件扩展名
    }),
    alias({
      entries: [{ find: '@', replacement: pathResolve('src') }]
    }), // 别名
    /* replace({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
     }), // process.env.NODE_ENV 通过 cross-env 依赖包在 scripts 打包指令处设置*/
    commonjs(), // [应该用在其他插件转换你的模块之前，不使用的话打包出的代码中会有 require(...) 这种 commonjs 的语法导入代码](https://www.rollupjs.com/guide/tools#rollup-plugin-commonjs)
    json(),
    fileSize(),
    // scss()
    visualizer(),
    sizes()
  ];
  if (frameConfig.extractCss) {
    plugins.splice(
      1,
      0,
      css({ output: `${frameConfig.outputFileNamePre}.css` })
    );
  }
  return {
    input: frameConfig.input, // 包的入口点
    external: [], // 指出应将哪些模块视为外部模块 external: ['axios', 'vue', 'lodash', 'lodash/max']，需要使用 CDN 外部载入
    plugins,
    output: {
      sourcemap: false, // 生成bundle.map.js文件，方便调试
      banner: createBanner(), // banner、footer 字符串以 前置/追加 到文件束(bundle)
      // footer: '/* follow me on gitee! @zhangh-design */',
      // 默认为true（当设置为是false，Rollup 不会生成代码来支持外部导入的实时绑定，而是假设导出不会随着时间的推移而改变。这将使 Rollup 能够生成更优化的代码）
      // https://www.rollupjs.org/guide/en/#outputexternallivebindings
      externalLiveBindings: false,
      globals: {
        // vue: 'Vue'
        // _max: 'lodash/max'
      } // 对应上面的 external
    }
  };
};

function mergeConfig(baseConfig, userConfig) {
  const config = Object.assign({}, baseConfig);
  // plugin
  if (userConfig.plugins) {
    baseConfig.plugins.push(...userConfig.plugins);
  }

  // output
  config.output = Object.assign({}, baseConfig.output, userConfig.output);

  //input
  if ('input' in userConfig) {
    config.input = userConfig.input;
  }
  // external
  if ('external' in userConfig) {
    config.external = userConfig.external;
  }
  // globals
  if (_has(userConfig, 'output.globals')) {
    config.output.globals = userConfig.output.globals;
  }
  return config;
}

// es-bundle （通过 webpack import 引入）
/**
 * es 构建模式在index.html中怎么导入使用：
 * <body>
 *   <script type="module">
 *    import test from './bundle.esm-bundler.js';
 *    test();
 *   </script>
 * </body>
 * 1：代码中如果引入 import _max from 'lodash/max'; 按需引入依赖，那么构建时不能设置 external: ['lodash/max']和 output.globals: {_max: 'lodash/max'}，
 *    我们需要将 lodash/max 这个依赖打包到我们的bundle中。
 * 2：如果最终输出的bundle文件是放在index.html中使用上述的形式导入那么我们在代码中这样引入lodash （import _ from 'lodash'; _.max([1,2,3])）
 *    就不能设置 external: [lodash]和output.globals: {_: 'lodash'}，因为index.html中cdn引入的lodash文件不是一个module，不能执行_.max函数
 * es 构建模式在 webpack 中怎么导入使用
 * import testFn from '@static/bundle.esm-bundler.js';
 * new Vue({
 *   created() {
 *    testFn();
 *   }
 * })
 * 1：如果我们的代码是这样写的import _ from 'lodash'; _.max([1,2,3])，最终输出的bundle文件是放在webpack中执行那么这里建议配置去除lodash，external: [lodash]和output.globals: {_: 'lodash'}，
 * 因为在webpack环境下安装了lodash依赖是可以执行_.max函数
 * 2：如果我们的代码是这样写的import _max from 'lodash/max'; _max([1,2,3])，那么建议配置去除external: ['lodash/max']和globals: { _max: 'lodash/max' }
 */
const esBundleConfig = {
  external: frameConfig.es.bundle.external, // esm 的模式可以移除 lodash/max，但如果是用在浏览器的script标签type=module模式的还是不能移除
  plugins: [
    replace({
      preventAssignment: true,
      exclude: 'node_modules/**',
      __DEV__: `(process.env.NODE_ENV !== 'production')`
    })
  ],
  output: {
    name: 'TrackingMd',
    file: createFileName('esm-bundler'),
    format: 'es',
    globals: frameConfig.es.bundle.globals
  }
};

// es-browser（通过 <script type=module> 标签引入）
const esBrowserConfig = {
  external: frameConfig.es.browser.external,
  plugins: [
    replace({
      preventAssignment: true,
      exclude: 'node_modules/**',
      __DEV__: true
    })
  ],
  output: {
    file: createFileName('esm-browser'),
    format: 'es',
    globals: frameConfig.es.browser.globals
  }
};

// es-browser.prod（通过 <script type=module> 标签引入）
const esBrowserProdConfig = {
  external: frameConfig.es.browser.external,
  plugins: [
    terser({
      compress: {
        pure_funcs: ['console.log', 'console.info'] // 去掉console.log函数
      }
    }),
    replace({
      preventAssignment: true,
      exclude: 'node_modules/**',
      __DEV__: false
    })
  ],
  output: {
    file: createFileName('esm-browser.min'),
    format: 'es',
    globals: frameConfig.es.browser.globals
  }
};

// cjs（CommonJS，适用于 Node 和 Browserify/Webpack）
const cjsConfig = {
  input: frameConfig.cjsInput, // 包的入口点
  external: frameConfig.cjs.external,
  plugins: [
    replace({
      preventAssignment: true,
      exclude: 'node_modules/**',
      __DEV__: true
    })
  ],
  output: {
    file: createFileName('cjs'),
    format: 'cjs',
    globals: frameConfig.cjs.globals
  }
};
// cjs.prod（CommonJS，适用于 Node 和 Browserify/Webpack）
const cjsProdConfig = {
  input: frameConfig.cjsInput, // 包的入口点
  external: frameConfig.cjs.external,
  plugins: [
    terser({
      compress: {
        pure_funcs: ['console.log', 'console.info'] // 去掉console.log函数
      }
    }),
    replace({
      preventAssignment: true,
      exclude: 'node_modules/**',
      __DEV__: false
    })
  ],
  output: {
    file: createFileName('cjs.min'),
    format: 'cjs',
    globals: frameConfig.cjs.globals
  }
};

// global（一个自动执行的功能，适合作为<script>标签）
/**
 * iife 的构建需要注意
 * 1：代码中如果引入 import _max from 'lodash/max'; 按需引入依赖，那么构建时不能设置 external: ['lodash/max']和 output.globals: {_max: 'lodash/max'}，
 *    我们需要将 lodash/max 这个依赖打包到我们的bundle中，不然在浏览器端script标签引入后执行代码会报 _max 没有定义的错误。
 * 2：代码中如果引入 import _ from 'lodash'; 那么我建议设置 external: ['lodash']和 output.globals: {_: 'lodash'} 将 lodash 在构建时移除出去，然后我们在
 *    index.html 页面中以cdn的形式引入lodash，如果不设置 external和globals 会使打包后的bundle体积变得很大因为将整个lodash也打包到了输出bundle中。
 * 所以如果在 createBaseConfig 基础配置中有移除 external: ['vue'] 和 globals: {Vue: 'vue'}，那么我们这里就不能写external: []和globals: {}
 * 需要这样写 external: ['vue'] 和 globals: {Vue: 'vue'}，我们不移除 lodash/max 但是 vue 的依赖我们还是需要移除。
 */
const globalConfig = {
  external: frameConfig.iife.external, // index.js 中使用了 import _max from 'lodash/max'; 所以这里不设置移除 external: ['lodash/max'] 和 globals: {_max: 'lodash/max'} 将 lodash/max 这个依赖打包到bundle文件中
  plugins: [
    replace({
      preventAssignment: true,
      __DEV__: true,
      'process.env.NODE_ENV': true,
      exclude: 'node_modules/**'
    })
  ],
  output: {
    file: createFileName('global'),
    format: 'iife',
    // script 导入的自执行文件需要一个 name，其余比如 cjs 模式不需要会使用 export 导出
    name: frameConfig.iife.outputName, // 开发版-不使用`terser`插件进行压缩，插件的对外全局变量（在index.html页面中引入打包的js文件后可以通过这个变量去调用内部的方法）
    globals: frameConfig.iife.globals
  }
};
// global.prod
const globalProdConfig = {
  external: frameConfig.iife.external,
  plugins: [
    terser({
      compress: {
        pure_funcs: ['console.log', 'console.info'] // 去掉console.log函数
      }
    }),
    replace({
      preventAssignment: true,
      exclude: 'node_modules/**',
      __DEV__: false
    })
  ],
  output: {
    file: createFileName('global.min'),
    format: 'iife',
    name: frameConfig.iife.outputName,
    globals: frameConfig.iife.globals
  }
};

// 开发环境
const formatDevConfigs = [
  esBundleConfig,
  esBrowserConfig,
  cjsConfig,
  globalConfig
];

// 生产环境
const formatProdConfigs = [
  esBrowserProdConfig,
  cjsProdConfig,
  globalProdConfig
];

/* const formatConfigs = [
  esBundleConfig,
  esBrowserProdConfig,
  esBrowserConfig,
  cjsConfig,
  cjsProdConfig,
  globalConfig,
  globalProdConfig
 ]; */
let formatConfigs = [];
if (process.env.NODE_ENV === 'dev') {
  formatConfigs = formatDevConfigs;
} else if (process.env.NODE_ENV === 'prod') {
  formatConfigs = formatProdConfigs;
} else {
  formatConfigs = _concat(formatDevConfigs, formatProdConfigs);
}

function createPackageConfigs() {
  return formatConfigs.map(formatConfig => {
    const configOption = mergeConfig(createBaseConfig(), formatConfig);
    return configOption;
  });
}

export default createPackageConfigs();
